<?php

declare(strict_types=1);
/**
 * This file is part of MoChat.
 * @link     https://mo.chat
 * @document https://mochat.wiki
 * @contact  group@mo.chat
 * @license  https://github.com/mochat-cloud/mochat/blob/master/LICENSE
 */
namespace MoChat\App\User\Logic;

use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Codec\Json;
use MoChat\App\Rbac\Contract\RbacMenuContract;
use MoChat\App\Rbac\Contract\RbacRoleContract;
use MoChat\App\Rbac\Contract\RbacRoleMenuContract;
use MoChat\App\Rbac\Contract\RbacUserRoleContract;
use MoChat\App\Tenant\Contract\TenantContract;
use MoChat\App\User\Contract\UserContract;
use MoChat\App\ValidationCode\Contract\ValidationCodeContract;
use MoChat\App\WorkEmployee\Contract\WorkEmployeeContract;
use MoChat\Framework\Constants\ErrorCode;
use MoChat\Framework\Exception\CommonException;
use MoChat\Plugin\AdminTenant\Action\Admin\Traits\OrderTrait;
use MoChat\Plugin\AdminUser\Contract\AdminUserContract;
use MoChat\Plugin\AdminUser\Contract\AdminUserLevelContract;
use MoChat\Plugin\TenantProduct\Contract\TenantProductSkuContract;
use Qbhy\HyperfAuth\AuthManager;
use Qbhy\SimpleJwt\JWTManager;

/**
 * 子账户管理- 创建提交.
 *
 * Class StoreLogic
 */
class ValidationCodeRegisterLogic
{
    use OrderTrait;

    /**
     * @Inject
     * @var UserContract
     */
    protected $userService;

    /**
     * @Inject
     * @var WorkEmployeeContract
     */
    protected $employeeService;

    /**
     * @Inject
     * @var RbacRoleMenuContract
     */
    protected $rbacRoleMenuService;

    /**
     * @Inject
     * @var RbacMenuContract
     */
    protected $rbacMenuservice;

    /**
     * @Inject
     * @var RbacUserRoleContract
     */
    protected $rbacUserRoleService;

    /**
     * @Inject
     * @var RbacRoleContract
     */
    protected $rbacRoleService;

    /**
     * @Inject
     * @var ValidationCodeContract
     */
    protected $validationCodeService;

    /**
     * @Inject
     * @var AdminUserContract
     */
    protected $adminUserService;

    /**
     * @Inject
     * @var AuthManager
     */
    protected $authManager;

    /**
     * @Inject
     * @var TenantContract
     */
    protected $tenantService;

    /**
     * @Inject
     * @var AuthManager
     */
    protected $auth;

    /**
     * @Inject
     * @var TenantProductSkuContract
     */
    protected $tenantProductSkuService;

    /**
     * @Inject
     * @var AdminUserLevelContract
     */
    protected $adminUserLevelService;

    /**
     * @Inject
     * @var UserContract
     */
    protected $user;

    /**
     * @Inject
     * @var StdoutLoggerInterface
     */
    private $logger;

    /**
     * @param array $params 请求参数
     * @return array 响应数组
     */
    public function handle(array $params): array
    {
        // 判断验证码
        $validationCode = $this->validationCodeService->getValidationCodeByPhoneCaptchaType($params['phone'], (int) $params['captcha'], 1);
        if (! $validationCode) {
            throw new CommonException(ErrorCode::INVALID_PARAMS, '验证码错误');
        }

        // 判断验证码是否过期
        if (time() - strtotime($validationCode['createdAt']) > 300) {
            throw new CommonException(ErrorCode::INVALID_PARAMS, '验证码已失效');
        }

        // 验证手机号
        $phoneUser = $this->userService->getUsersByPhone([$params['phone']], ['id']);
        if (! empty($phoneUser)) {
            throw new CommonException(ErrorCode::INVALID_PARAMS, '手机号已存在，不可重复创建');
        }

        // 判断是否有超级管理员
        $adminUser = $this->adminUserService->getAdminUserByIsRoot(1);
        if (! $adminUser) {
            throw new CommonException(ErrorCode::SERVER_ERROR, '当前系统内没有超级管理员，请联系网站管理员');
        }

        // 判断是否有默认套餐
        $sku = $this->tenantProductSkuService->getTenantProductAttributesByName('默认套餐', ['id']);

        $valueId = 0;
        foreach ($sku as $k => $v) {
            $res = $this->tenantProductSkuService->getTenantProductValueByAttributeId($v['id']);
            if ($res['status'] == 1) {
                $valueId = $res['id'];
                break;
            }
        }

        if (! $valueId) {
            throw new CommonException(ErrorCode::SERVER_ERROR, '未设置默认套餐');
        }

        $sku = $this->tenantProductSkuService->getTenantProductSkuByAttributeId($valueId, ['id']);

        if (! $sku) {
            throw new CommonException(ErrorCode::SERVER_ERROR, '未设置默认套餐');
        }

        $params['skuId'] = $sku['id'];

        // 处理数据
        $params = $this->handleParams($params);

        // 数据入表
        $this->insertData($params, $adminUser);

        // 更改验证码状态
        $this->validationCodeService->updateValidationCodeStatusById($validationCode['id'], 1);

        // 获取用户
        $userData = $this->user->getUserByPhone($params['phone'], ['id', 'status', 'password', 'tenant_id']);

        // 逻辑处理
        $guard = $this->auth->guard('jwt');
        /** @var JWTManager $jwt */
        $jwt = $guard->getJwtManager();
        // 响应参数
        return [
            'token' => $guard->login($userData),
            'expire' => $jwt->getTtl(),
        ];
    }

    /**
     * @param array $params 请求参数
     * @return array 响应数组
     */
    private function handleParams(array $params): array
    {
        # # 生成初始密码
        $guard = $this->authManager->guard('jwt');
        /** @var JWTManager $jwt */
        $jwt = $guard->getJwtManager();
        $params['password'] = $jwt->getEncrypter()->signature($params['password']);
        $params['created_at'] = date('Y-m-d H:i:s');

        return $params;
    }

    /**
     * @param array $params 请求参数
     * @param array $adminUser 管理员信息
     */
    private function insertData(array $params, array $adminUser)
    {
        Db::beginTransaction();
        try {
            // 用户id
            $userId = $this->userService->createUser($params);

            // 租户id
            $tenantId = $this->insertTenantData($params, $userId, $params['skuId']);

            $this->userService->updateUserById($userId, ['tenant_id' => $tenantId]);

            $role = $this->insertRoleData($tenantId, $userId);

            $this->rbacUserRoleService->createRbacUserRoles($role);

            // 余额检查
            $order = $this->handleParamsOrder($params, $adminUser);

            // 订单&代理金币处理
            $this->order($params['skuId'], $order, $tenantId, $adminUser);

            $this->insertMenu($order['roleMenu'], $role);

            // 创建租户权限并设置过期时间
            $this->tenantService->updateTenantById($tenantId, ['role_menu' => $order['roleMenu'], 'expired_at' => date('Y-m-d', strtotime("+{$order['cycle']} day"))]);

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            $this->logger->error(sprintf('%s [%s] %s', '账户创建失败', date('Y-m-d H:i:s'), $e->getMessage()));
            $this->logger->error($e->getTraceAsString());
            throw new CommonException(ErrorCode::SERVER_ERROR, '账户创建失败');
        }
    }

    /**
     * @param array $params 请求参数
     * @param int $userId 用户id
     * @param int $skuId 套餐id
     */
    private function insertTenantData(array $params, int $userId, int $skuId): int
    {
        $data = [
            'name' => '手机号为' . $params['phone'] . '的租户',
            'phone' => $params['phone'],
            'sku_id' => $skuId,
            'root_id' => $userId,
            'created_at' => date('Y-m-d H:i:s'),
        ];

        return $this->tenantService->createTenant($data);
    }

    private function handleParamsOrder(array $params, array $adminUser): array
    {
        $order = ['walletBalance' => 0, 'itemTotal' => 0, 'adjustmentTotal' => 0, 'total' => 0, 'stock' => 0, 'cycle' => 1, 'roleMenu' => '', 'userId' => $adminUser['id']];

        // 代理金币余额
        $user = $this->adminUserService->getAdminUserById($adminUser['id'], ['level', 'wallet_balance']);
        $order['walletBalance'] = $user['walletBalance'];

        // 套餐价格
        $sku = $this->tenantProductSkuService->getTenantProductSkuById($params['skuId'], ['attributes', 'price', 'stock']);
        $attributes = $this->tenantProductSkuService->getTenantProductSkuValuesById(explode(',', $sku['attributes']));
        foreach ($attributes as $v) {
            if ($v['attributeName'] === '服务周期') {
                $order['cycle'] = $v['attributeValue'];
            }
            if ($v['attributeName'] === '套餐功能') {
                $order['roleMenu'] = $v['attributeValue'];
            }
        }
        $order['stock'] = $sku['stock'];
        $order['itemTotal'] = $sku['price'] * 100;
        if ($sku['stock'] <= 0) {
            throw new CommonException(ErrorCode::INVALID_PARAMS, '套餐库存数量不足，请联系管理员');
        }

        // 代理扣币规则
        $level = $this->adminUserLevelService->getAdminUserLevelById($user['level'], ['price_rule']);
        $priceRule = Json::decode($level['priceRule']);

        // 统一折扣
        if ((int) $priceRule['type'] === 1) {
            $price = number_format($sku['price'] * $priceRule['discount'] / 10, 2);
            $order['adjustmentTotal'] = ($sku['price'] - $price) * 100;
            $order['total'] = $price * 100;
            if ($user['walletBalance'] < $price) {
                throw new CommonException(ErrorCode::INVALID_PARAMS, '金币余额不足以支付租户套餐费用，请充值后创建');
            }
        }

        // 按套餐扣币
        if ((int) $priceRule['type'] === 2) {
            if (in_array($user['level'], array_column($priceRule['sku'], 'id'), true)) {
                foreach ($priceRule['sku'] as $item) {
                    if ($user['level'] == $item['id']) {
                        $price = $item['price'];
                        $order['adjustmentTotal'] = ($sku['price'] - $price) * 100;
                        $order['total'] = $price * 100;
                        if ($user['walletBalance'] < $item['price']) {
                            throw new CommonException(ErrorCode::INVALID_PARAMS, '金币余额不足以支付租户套餐费用，请充值后创建');
                        }
                    }
                }
            } else {
                $price = $sku['price'];
                $order['total'] = $price * 100;
                if ($user['walletBalance'] < $sku['price']) {
                    throw new CommonException(ErrorCode::INVALID_PARAMS, '金币余额不足以支付租户套餐费用，请充值后创建');
                }
            }
        }
        return $order;
    }

    /**
     * 初始化角色.
     * @param int $tenantId 租户id
     * @param int $userId 用户id
     */
    private function insertRoleData(int $tenantId, int $userId): array
    {
        $roleArr = ['管理员', '部门管理员', '普通员工'];
        $role = [];
        $user = $this->userService->getUserById($userId, ['name']);
        foreach ($roleArr as $k => $v) {
            $role[$k]['role_id'] = $this->rbacRoleService->createRbacRole(['name' => $v, 'tenant_id' => $tenantId, 'operate_id' => $userId, 'created_at' => date('Y-m-d H:i:s'), 'operate_name' => $user['name']]);
            $role[$k]['user_id'] = $userId;
            $role[$k]['created_at'] = date('Y-m-d H:i:s');
        }

        return $role;
    }

    /**
     * 添加角色权限.
     * @param string $roleMenu 租户权限
     * @param array $role 角色
     */
    private function insertMenu(string $roleMenu, array $role)
    {
        // 租户默认菜单
        $tenantMenuIds = explode(',', $roleMenu);
        // 管理员默认菜单id
        $AdminMenuIdsFour = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($tenantMenuIds, 4, ['id']), 'id');
        $AdminMenuIdsFive = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($AdminMenuIdsFour, 5, ['id']), 'id');
        $adminMenuIds = array_unique(array_merge($AdminMenuIdsFive, $AdminMenuIdsFour, $tenantMenuIds));

        // 部门管理员默认菜单id
        $departmentAdminMenuIds = [1, 334, 335];
        $departmentAdminMenuFour = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($departmentAdminMenuIds, 4, ['id']), 'id');
        $departmentAdminMenuFive = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($departmentAdminMenuFour, 5, ['id']), 'id');
        $departmentAdminMenuIds = array_unique(array_merge($departmentAdminMenuIds, $departmentAdminMenuFour, $departmentAdminMenuFive));

        // 普通员工默认菜单id
        $defaultEmployeeMenuIds = [1, 334, 335];
        $defaultEmployeeMenuFour = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($defaultEmployeeMenuIds, 4, ['id']), 'id');
        $defaultEmployeeMenuFive = array_column($this->rbacMenuservice->getRbacMenusByPatentIdLevel($defaultEmployeeMenuFour, 5, ['id']), 'id');
        $defaultEmployeeMenuIds = array_unique(array_merge($defaultEmployeeMenuIds, $defaultEmployeeMenuFive, $defaultEmployeeMenuFour));

        $roleMenuArr = [];
        foreach ($role as $k => $v) {
            if ($k == 0) {
                foreach ($adminMenuIds as $k1 => $v1) {
                    $roleMenuArr[] = [
                        'role_id' => $v['role_id'],
                        'menu_id' => (int) $v1,
                    ];
                }
            } elseif ($k == 1) {
                foreach ($departmentAdminMenuIds as $k1 => $v1) {
                    $roleMenuArr[] = [
                        'role_id' => $v['role_id'],
                        'menu_id' => (int) $v1,
                    ];
                }
            } elseif ($k == 2) {
                foreach ($defaultEmployeeMenuIds as $k1 => $v1) {
                    $roleMenuArr[] = [
                        'role_id' => $v['role_id'],
                        'menu_id' => (int) $v1,
                    ];
                }
            }
        }

        return $this->rbacRoleMenuService->createRbacRoleMenus($roleMenuArr);
    }
}
